////////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2008 Michiel Helvensteijn                                    //
//                                                                            //
// This file is part of the Mist compiler.                                    //
//                                                                            //
// The Mist compiler is free software: you can redistribute it and/or modify  //
// it under the terms of the GNU General Public License as published by       //
// the Free Software Foundation, either version 3 of the License, or          //
// (at your option) any later version.                                        //
//                                                                            //
// The Mist compiler is distributed in the hope that it will be useful,       //
// but WITHOUT ANY WARRANTY; without even the implied warranty of             //
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the              //
// GNU General Public License for more details.                               //
//                                                                            //
// You should have received a copy of the GNU General Public License          //
// along with the Mist compiler.  If not, see <http://www.gnu.org/licenses/>. //
////////////////////////////////////////////////////////////////////////////////

#include "message.hpp"

/******************************************************************************
 * Includes                                                                   *
 ******************************************************************************/

#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
using boost::make_tuple;

#include "common.hpp"
#include "assert.hpp"
#include "messageinfo.hpp"

/******************************************************************************
 * Function Implementations                                                   *
 ******************************************************************************/

Message::Message(MessageSeverity severity, const string& message) : _code(""), _severity(severity), _locationIsSet(false), _message(message) {
	assert(!message.empty());
}

Message::Message(MessageSeverity severity, CodeLocation location, const string& message) : _code(""), _severity(severity), _locationIsSet(true), _location(location), _message(message) {
	assert(!message.empty());
}

Message::Message(const string& code) : _code(code), _locationIsSet(false) {
	assert(the(MessageInfo).code_exists(code));
	
	_message.parse(the(MessageInfo).message(code));
	_severity = the(MessageInfo).severity(code);
}

Message::Message(const string& code, CodeLocation location) : _code(code), _locationIsSet(true), _location(location) {
	assert(the(MessageInfo).code_exists(code));
	
	_message.parse(the(MessageInfo).message(code));
	_severity = the(MessageInfo).severity(code);
}

const string& Message::code() const {
	assert(code_is_set());
	
	return _code;
}

bool Message::code_is_set() const {
	return !_code.empty();
}

MessageSeverity Message::severity() const {
	return _severity;
}

bool Message::location_is_set() const {
	return _locationIsSet;
}

const CodeLocation& Message::location() const {
	assert(location_is_set());
	
	return _location;
}

string Message::message() const {
	return _message.str();
}

ostream& Message::report_to_stream(ostream& out) const {
	switch(severity()) {
		case Error:   out << "Error: "; break;
		case Warning: out << "Warning: "; break;
		default:      assert(false);
	}
	
	if (location_is_set())
		out << location() << "\n    ";
	
	out << _message << '\n';
	
	return out;
}

bool Message::operator<(const Message& other) const {
	if (location_is_set() && other.location_is_set())
		return make_tuple(_location, _code)
			< make_tuple(other._location, other._code);
	else if (location_is_set() && !other.location_is_set())
		return false;
	else if (!location_is_set() && other.location_is_set())
		return true;
	else
		return (_code < other._code);
}

bool Message::operator==(const Message& other) const {
	return (_location == other._location && _code == other._code && _message.str() == other._message.str());
}

ostream& operator<<(ostream& out, const Message& message) {
	return message.report_to_stream(out);
}
